// This file is part of Substrate.

// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Dispatch system. Contains a macro for defining runtime modules and
//! generating values representing lazy module function calls.

pub use crate::sp_std::{result, fmt, prelude::{Vec, Clone, Eq, PartialEq}, marker};
pub use crate::codec::{Codec, EncodeLike, Decode, Encode, Input, Output, HasCompact, EncodeAsRef};
pub use frame_metadata::{
	FunctionMetadata, DecodeDifferent, DecodeDifferentArray, FunctionArgumentMetadata,
	ModuleConstantMetadata, DefaultByte, DefaultByteGetter, ModuleErrorMetadata, ErrorMetadata
};
pub use crate::weights::{
	GetDispatchInfo, DispatchInfo, WeighData, ClassifyDispatch, TransactionPriority, Weight,
	PaysFee, PostDispatchInfo, WithPostDispatchInfo,
};
pub use sp_runtime::{traits::Dispatchable, DispatchError};
pub use crate::traits::{CallMetadata, GetCallMetadata, GetCallName};

/// The return typ of a `Dispatchable` in frame. When returned explicitly from
/// a dispatchable function it allows overriding the default `PostDispatchInfo`
/// returned from a dispatch.
pub type DispatchResultWithPostInfo =
	sp_runtime::DispatchResultWithInfo<crate::weights::PostDispatchInfo>;

/// Unaugmented version of `DispatchResultWithPostInfo` that can be returned from
/// dispatchable functions and is automatically converted to the augmented type. Should be
/// used whenever the `PostDispatchInfo` does not need to be overwritten. As this should
/// be the common case it is the implicit return type when none is specified.
pub type DispatchResult = Result<(), sp_runtime::DispatchError>;

/// The error type contained in a `DispatchResultWithPostInfo`.
pub type DispatchErrorWithPostInfo =
	sp_runtime::DispatchErrorWithPostInfo<crate::weights::PostDispatchInfo>;

/// Serializable version of Dispatchable.
/// This value can be used as a "function" in an extrinsic.
pub trait Callable<T> {
	type Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + Codec + Clone + PartialEq + Eq;
}

// dirty hack to work around serde_derive issue
// https://github.com/rust-lang/rust/issues/51331
pub type CallableCallFor<A, T> = <A as Callable<T>>::Call;

/// A type that can be used as a parameter in a dispatchable function.
///
/// When using `decl_module` all arguments for call functions must implement this trait.
pub trait Parameter: Codec + EncodeLike + Clone + Eq + fmt::Debug {}
impl<T> Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {}

/// Declares a `Module` struct and a `Call` enum, which implements the dispatch logic.
///
/// ## Declaration
///
/// ```
/// # #[macro_use]
/// # extern crate frame_support;
/// # use frame_support::dispatch;
/// # use frame_system::{self as system, Trait, ensure_signed};
/// decl_module! {
/// 	pub struct Module<T: Trait> for enum Call where origin: T::Origin {
///
/// 		// Private functions are dispatchable, but not available to other
/// 		// FRAME pallets.
/// 		#[weight = 0]
/// 		fn my_function(origin, var: u64) -> dispatch::DispatchResult {
///				// Your implementation
///				Ok(())
/// 		}
///
///			// Public functions are both dispatchable and available to other
/// 		// FRAME pallets.
/// 		#[weight = 0]
///			pub fn my_public_function(origin) -> dispatch::DispatchResult {
/// 			// Your implementation
///				Ok(())
/// 		}
///		}
/// }
/// # fn main() {}
/// ```
///
/// The declaration is set with the header where:
///
/// * `Module`: The struct generated by the macro, with type `Trait`.
/// * `Call`: The enum generated for every pallet, which implements [`Callable`](./dispatch/trait.Callable.html).
/// * `origin`: Alias of `T::Origin`, declared by the [`impl_outer_origin!`](./macro.impl_outer_origin.html) macro.
/// * `Result`: The expected return type from pallet functions.
///
/// The first parameter of dispatchable functions must always be `origin`.
///
/// ### Shorthand Example
///
/// The macro automatically expands a shorthand function declaration to return the
/// [`DispatchResult`](dispatch::DispatchResult) type. These functions are the same:
///
/// ```
/// # #[macro_use]
/// # extern crate frame_support;
/// # use frame_support::dispatch;
/// # use frame_system::{self as system, Trait, ensure_signed};
/// decl_module! {
/// 	pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// 		#[weight = 0]
/// 		fn my_long_function(origin) -> dispatch::DispatchResult {
///				// Your implementation
/// 			Ok(())
/// 		}
///
/// 		#[weight = 0]
/// 		fn my_short_function(origin) {
///				// Your implementation
/// 		}
///		}
/// }
/// # fn main() {}
/// ```
///
/// ### Consuming only portions of the annotated static weight
///
/// Per default a callable function consumes all of its static weight as declared via
/// the #\[weight\] attribute. However, there are use cases where only a portion of this
/// weight should be consumed. In that case the static weight is charged pre dispatch and
/// the difference is refunded post dispatch.
///
/// In order to make use of this feature the function must return `DispatchResultWithPostInfo`
/// in place of the default `DispatchResult`. Then the actually consumed weight can be returned.
/// To consume a non default weight while returning an error
/// [`WithPostDispatchInfo::with_weight`](./weight/trait.WithPostDispatchInfo.html) can be used
/// to augment any error with custom weight information.
///
/// ```
/// # #[macro_use]
/// # extern crate frame_support;
/// # use frame_support::dispatch::{DispatchResultWithPostInfo, WithPostDispatchInfo};
/// # use frame_system::{self as system, Trait, ensure_signed};
/// decl_module! {
/// 	pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// 		#[weight = 1_000_000]
/// 		fn my_long_function(origin, do_expensive_calc: bool) -> DispatchResultWithPostInfo {
/// 			ensure_signed(origin).map_err(|e| e.with_weight(100_000))?;
/// 			if do_expensive_calc {
/// 				// do the expensive calculation
/// 				// ...
/// 				// return None to indicate that we are using all weight (the default)
/// 				return Ok(None.into());
/// 			}
/// 			// expensive calculation not executed: use only a portion of the weight
/// 			Ok(Some(100_000).into())
/// 		}
/// 	}
/// }
/// # fn main() {}
/// ```
///
/// ### Privileged Function Example
///
/// A privileged function checks that the origin of the call is `ROOT`.
///
/// ```
/// # #[macro_use]
/// # extern crate frame_support;
/// # use frame_support::dispatch;
/// # use frame_system::{self as system, Trait, ensure_signed, ensure_root};
/// decl_module! {
/// 	pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// 		#[weight = 0]
///			fn my_privileged_function(origin) -> dispatch::DispatchResult {
/// 			ensure_root(origin)?;
///				// Your implementation
/// 			Ok(())
/// 		}
///		}
/// }
/// # fn main() {}
/// ```
///
/// ## Multiple Module Instances Example
///
/// A Substrate module can be built such that multiple instances of the same module can be used within a single
/// runtime. For example, the [Balances module](../pallet_balances/index.html) can be added multiple times to your
/// runtime in order to support multiple, independent currencies for your blockchain. Here is an example of how
/// you would declare such a module using the `decl_module!` macro:
///
/// ```
/// # #[macro_use]
/// # extern crate frame_support;
/// # use frame_support::dispatch;
/// # use frame_system::{self as system, ensure_signed};
/// # pub struct DefaultInstance;
/// # pub trait Instance {}
/// # impl Instance for DefaultInstance {}
/// pub trait Trait<I: Instance=DefaultInstance>: system::Trait {}
///
/// decl_module! {
/// 	pub struct Module<T: Trait<I>, I: Instance = DefaultInstance> for enum Call where origin: T::Origin {
/// 		// Your implementation
/// 	}
/// }
/// # fn main() {}
/// ```
///
/// Note: `decl_storage` must be called to generate `Instance` trait and optionally
/// `DefaultInstance` type.
///
/// ## Where clause
///
/// Besides the default `origin: T::Origin`, you can also pass other bounds to the module declaration.
/// This where bound will be replicated to all types generated by this macro. The chaining of multiple
/// trait bounds with `+` is not supported. If multiple bounds for one type are required, it needs to
/// be split up into multiple bounds.
///
/// ```
/// # #[macro_use]
/// # extern crate frame_support;
/// # use frame_support::dispatch;
/// # use frame_system::{self as system, ensure_signed};
/// pub trait Trait: system::Trait where Self::AccountId: From<u32> {}
///
/// decl_module! {
/// 	pub struct Module<T: Trait> for enum Call where origin: T::Origin, T::AccountId: From<u32> {
/// 		// Your implementation
/// 	}
/// }
/// # fn main() {}
/// ```
///
/// ## Reserved Functions
///
/// The following are reserved function signatures:
///
/// * `deposit_event`: Helper function for depositing an [event](https://docs.substrate.dev/docs/event-enum).
/// The default behavior is to call `deposit_event` from the [System module](../frame_system/index.html).
/// However, you can write your own implementation for events in your runtime. To use the default behavior,
/// add `fn deposit_event() = default;` to your `Module`.
///
/// The following reserved functions also take the block number (with type `T::BlockNumber`) as an optional input:
///
/// * `on_runtime_upgrade`: Executes at the beginning of a block prior to on_initialize when there
/// is a runtime upgrade. This allows each module to upgrade its storage before the storage items are used.
/// As such, **calling other modules must be avoided**!! Using this function will implement the
/// [`OnRuntimeUpgrade`](../sp_runtime/traits/trait.OnRuntimeUpgrade.html) trait.
/// Function signature must be `fn on_runtime_upgrade() -> frame_support::weights::Weight`.
///
/// * `on_initialize`: Executes at the beginning of a block. Using this function will
/// implement the [`OnInitialize`](./trait.OnInitialize.html) trait.
/// Function signature can be either:
///   * `fn on_initialize(n: BlockNumber) -> frame_support::weights::Weight` or
///   * `fn on_initialize() -> frame_support::weights::Weight`
///
/// * `on_finalize`: Executes at the end of a block. Using this function will
/// implement the [`OnFinalize`](./traits/trait.OnFinalize.html) trait.
/// Function signature can be either:
///   * `fn on_finalize(n: BlockNumber) -> frame_support::weights::Weight` or
///   * `fn on_finalize() -> frame_support::weights::Weight`
///
/// * `offchain_worker`: Executes at the beginning of a block and produces extrinsics for a future block
/// upon completion. Using this function will implement the
/// [`OffchainWorker`](./traits/trait.OffchainWorker.html) trait.
#[macro_export]
macro_rules! decl_module {
	// Entry point #1.
	(
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident
			$( <I>, I: $instantiable:path $( = $module_default_instance:path )? )?
		>
		for enum $call_type:ident where origin: $origin_type:ty $(, $where_ty:ty: $where_bound:path )* {
			$( $t:tt )*
		}
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<
				$trait_instance: $trait_name $(<I>, I: $instantiable $(= $module_default_instance)?)?
			>
			for enum $call_type where origin: $origin_type, system = system
			{ $( $where_ty: $where_bound ),* }
			{}
			{}
			{}
			{}
			{}
			{}
			{}
			[]
			$($t)*
		);
	};
	// Entry point #2.
	(
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident
			$( <I>, I: $instantiable:path $( = $module_default_instance:path )? )?
		>
		for enum $call_type:ident where
			origin: $origin_type:ty,
			system = $system:ident
			$(, $where_ty:ty: $where_bound:path )*
		{
			$($t:tt)*
		}
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<
				$trait_instance: $trait_name $(<I>, I: $instantiable $( = $module_default_instance )? )?
			>
			for enum $call_type where origin: $origin_type, system = $system
			{ $( $where_ty: $where_bound ),* }
			{}
			{}
			{}
			{}
			{}
			{}
			{}
			[]
			$($t)*
		);
	};

	// Normalization expansions. Fills the defaults.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{}
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		$vis:vis fn deposit_event() = default;
		$($rest:tt)*
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<$trait_instance: $trait_name$(<I>, I: $instantiable $(= $module_default_instance)?)?>
			for enum $call_type where origin: $origin_type, system = $system
			{ $( $other_where_bounds )* }
			{ $vis fn deposit_event() = default; }
			{ $( $on_initialize )* }
			{ $( $on_runtime_upgrade )* }
			{ $( $on_finalize )* }
			{ $( $offchain )* }
			{ $( $constants )* }
			{ $( $error_type )* }
			[ $( $dispatchables )* ]
			$($rest)*
		);
	};
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{}
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		$vis:vis fn deposit_event
		$($rest:tt)*
	) => {
		compile_error!(
			"`deposit_event` function is reserved and must follow the syntax: `$vis:vis fn deposit_event() = default;`"
		);
	};
	// Add on_finalize
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{}
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		fn on_finalize( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<$trait_instance: $trait_name$(<I>, I: $instantiable $(= $module_default_instance)?)?>
			for enum $call_type where origin: $origin_type, system = $system
			{ $( $other_where_bounds )* }
			{ $( $deposit_event )* }
			{ $( $on_initialize )* }
			{ $( $on_runtime_upgrade )* }
			{
				fn on_finalize( $( $param_name : $param ),* ) { $( $impl )* }
			}
			{ $( $offchain )* }
			{ $( $constants )* }
			{ $( $error_type )* }
			[ $( $dispatchables )* ]
			$($rest)*
		);
	};
	// compile_error on_finalize, given weight removed syntax.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{}
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		#[weight = $weight:expr]
		fn on_finalize( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		compile_error!(
			"`on_finalize` can't be given weight attribute anymore, weight must be returned by \
			`on_initialize` or `on_runtime_upgrade` instead"
		);
	};
	// compile_error on_runtime_upgrade, without a given weight removed syntax.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?
		>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{}
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		fn on_runtime_upgrade( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		compile_error!(
			"`on_runtime_upgrade` must return Weight, signature has changed."
		);
	};
	// compile_error on_runtime_upgrade, given weight removed syntax.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?
		>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{}
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		#[weight = $weight:expr]
		fn on_runtime_upgrade( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		compile_error!(
			"`on_runtime_upgrade` can't be given weight attribute anymore, weight must be returned \
			by the function directly."
		);
	};
	// Add on_runtime_upgrade
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?
		>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{}
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		fn on_runtime_upgrade( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<$trait_instance: $trait_name$(<I>, I: $instantiable $(= $module_default_instance)?)?>
			for enum $call_type where origin: $origin_type, system = $system
			{ $( $other_where_bounds )* }
			{ $( $deposit_event )* }
			{ $( $on_initialize )* }
			{
				fn on_runtime_upgrade( $( $param_name : $param ),* ) -> $return { $( $impl )* }
			}
			{ $( $on_finalize )* }
			{ $( $offchain )* }
			{ $( $constants )* }
			{ $( $error_type )* }
			[ $( $dispatchables )* ]
			$($rest)*
		);
	};
	// compile_error on_initialize, without a given weight removed syntax.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?
		>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{}
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		fn on_initialize( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		compile_error!(
			"`on_initialize` must return Weight, signature has changed."
		);
	};
	// compile_error on_initialize, with given weight removed syntax.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?
		>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{}
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		#[weight = $weight:expr]
		fn on_initialize( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		compile_error!(
			"`on_initialize` can't be given weight attribute anymore, weight must be returned \
			by the function directly."
		);
	};
	// Add on_initialize
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?
		>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{}
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		fn on_initialize( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<$trait_instance: $trait_name$(<I>, I: $instantiable $(= $module_default_instance)?)?>
			for enum $call_type where origin: $origin_type, system = $system
			{ $( $other_where_bounds )* }
			{ $( $deposit_event )* }
			{
				fn on_initialize( $( $param_name : $param ),* ) -> $return { $( $impl )* }
			}
			{ $( $on_runtime_upgrade )* }
			{ $( $on_finalize )* }
			{ $( $offchain )* }
			{ $( $constants )* }
			{ $( $error_type )* }
			[ $( $dispatchables )* ]
			$($rest)*
		);
	};
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident
			$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?
		>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		fn offchain_worker( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<
				$trait_instance: $trait_name$(<I>, I: $instantiable $(= $module_default_instance)?)?
			>
			for enum $call_type where origin: $origin_type, system = $system
			{ $( $other_where_bounds )* }
			{ $( $deposit_event )* }
			{ $( $on_initialize )* }
			{ $( $on_runtime_upgrade )* }
			{ $( $on_finalize )* }
			{ fn offchain_worker( $( $param_name : $param ),* ) { $( $impl )* } }
			{ $( $constants )* }
			{ $( $error_type )* }
			[ $( $dispatchables )* ]
			$($rest)*
		);
	};

	// This puts a constant in the parsed constants list.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident
			$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?
		>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$( #[doc = $doc_attr:tt] )*
		const $name:ident: $ty:ty = $value:expr;
		$( $rest:tt )*
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<
				$trait_instance: $trait_name
				$( <I>, $instance: $instantiable $(= $module_default_instance)? )?
			>
			for enum $call_type where origin: $origin_type, system = $system
			{ $( $other_where_bounds )* }
			{ $( $deposit_event )* }
			{ $( $on_initialize )* }
			{ $( $on_runtime_upgrade )* }
			{ $( $on_finalize )* }
			{ $( $offchain )* }
			{
				$( $constants )*
				$( #[doc = $doc_attr ] )*
				$name: $ty = $value;
			}
			{ $( $error_type )* }
			[ $( $dispatchables )* ]
			$($rest)*
		);
	};

	// Parse error type
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident:
				$trait_name:ident$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?
			>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		type Error = $error_type:ty;
		$($rest:tt)*
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<
				$trait_instance: $trait_name$(<I>, $instance: $instantiable $(= $module_default_instance)?)?
			>
			for enum $call_type where origin: $origin_type, system = $system
			{ $( $other_where_bounds )* }
			{ $( $deposit_event )* }
			{ $( $on_initialize )* }
			{ $( $on_runtime_upgrade )* }
			{ $( $on_finalize )* }
			{ $( $offchain )* }
			{ $( $constants )* }
			{ $error_type }
			[ $( $dispatchables )* ]
			$($rest)*
		);
	};
	// Add default Error if none supplied
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident:
				$trait_name:ident$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?
			>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ }
		[ $($t:tt)* ]
		$($rest:tt)*
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<
				$trait_instance: $trait_name$(<I>, $instance: $instantiable $(= $module_default_instance)?)?
			>
			for enum $call_type where origin: $origin_type, system = $system
			{ $( $other_where_bounds )* }
			{ $( $deposit_event )* }
			{ $( $on_initialize )* }
			{ $( $on_runtime_upgrade )* }
			{ $( $on_finalize )* }
			{ $( $offchain )* }
			{ $( $constants )* }
			{ &'static str }
			[ $($t)* ]
			$($rest)*
		);
	};

	// This puts the function statement into the [], decreasing `$rest` and moving toward finishing the parse.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident
			$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?
			>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $error_type:ty }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		#[weight = $weight:expr]
		$fn_vis:vis fn $fn_name:ident(
			$origin:ident $( , $(#[$codec_attr:ident])* $param_name:ident : $param:ty )* $(,)?
		) $( -> $result:ty )* { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		$crate::decl_module!(@normalize
			$(#[$attr])*
			pub struct $mod_type<
				$trait_instance: $trait_name$(<I>, $instance: $instantiable $(= $module_default_instance)?)?
			>
			for enum $call_type where origin: $origin_type, system = $system
			{ $( $other_where_bounds )* }
			{ $( $deposit_event )* }
			{ $( $on_initialize )* }
			{ $( $on_runtime_upgrade )* }
			{ $( $on_finalize )* }
			{ $( $offchain )* }
			{ $( $constants )* }
			{ $error_type }
			[
				$( $dispatchables )*
				$(#[doc = $doc_attr])*
				#[weight = $weight]
				$fn_vis fn $fn_name(
					$origin $( , $(#[$codec_attr])* $param_name : $param )*
				) $( -> $result )* { $( $impl )* }
				{ $($instance: $instantiable)? }
			]
			$($rest)*
		);
	};
	// Add #[weight] if none is defined.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident:
				$trait_name:ident$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?
			>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		$fn_vis:vis fn $fn_name:ident(
			$from:ident $( , $( #[$codec_attr:ident] )* $param_name:ident : $param:ty )* $(,)?
		) $( -> $result:ty )* { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		compile_error!(concat!(
			"Missing weight for ", stringify!($ident),
			". Every dispatchable must have a #[weight] attribute."
			)
		);
	};
	// Ignore any ident which is not `origin` with type `T::Origin`.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		$(#[weight = $weight:expr])?
		$fn_vis:vis fn $fn_name:ident(
			$origin:ident : T::Origin $( , $( #[$codec_attr:ident] )* $param_name:ident : $param:ty )* $(,)?
		) $( -> $result:ty )* { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		compile_error!(
			"First parameter of dispatch should be marked `origin` only, with no type specified \
			(a bit like `self`)."
		);
	};
	// Ignore any ident which is `origin` but has a type, regardless of the type token itself.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		$(#[weight = $weight:expr])?
		$fn_vis:vis fn $fn_name:ident(
			origin : $origin:ty $( , $( #[$codec_attr:ident] )* $param_name:ident : $param:ty )* $(,)?
		) $( -> $result:ty )* { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		compile_error!(
			"First parameter of dispatch should be marked `origin` only, with no type specified \
			(a bit like `self`)."
		);
	};
	// Ignore any function missing `origin` as the first parameter.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
		$(#[doc = $doc_attr:tt])*
		$(#[weight = $weight:expr])?
		$fn_vis:vis fn $fn_name:ident(
			$( $(#[$codec_attr:ident])* $param_name:ident : $param:ty ),* $(,)?
		) $( -> $result:ty )* { $( $impl:tt )* }
		$($rest:tt)*
	) => {
		compile_error!(
			"Implicit conversion to privileged function has been removed. \
			First parameter of dispatch should be marked `origin`. \
			For root-matching dispatch, also add `ensure_root(origin)?`."
		);
	};
	// Last normalize step. Triggers `@imp` expansion which is the real expansion.
	(@normalize
		$(#[$attr:meta])*
		pub struct $mod_type:ident<$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path $(= $module_default_instance:path)?)?>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $( $error_type:tt )* }
		[ $( $dispatchables:tt )* ]
	) => {
		$crate::decl_module!(@imp
			$(#[$attr])*
			pub struct $mod_type<$trait_instance: $trait_name$(<I>, I: $instantiable $(= $module_default_instance)?)?>
			for enum $call_type where origin: $origin_type, system = $system {
				$( $dispatchables )*
			}
			{ $( $other_where_bounds )* }
			{ $( $deposit_event )* }
			{ $( $on_initialize )* }
			{ $( $on_runtime_upgrade )* }
			{ $( $on_finalize )* }
			{ $( $offchain )* }
			{ $( $constants )* }
			{ $( $error_type )* }
		);
	};

	// Implementation of Call enum's .dispatch() method.
	// TODO: this probably should be a different macro?

	(@call
		$ignore:ident
		$mod_type:ident<$trait_instance:ident $(, $instance:ident)?> $fn_name:ident $origin:ident $system:ident [ $( $param_name:ident),* ]
	) => {
		<$mod_type<$trait_instance $(, $instance)?>>::$fn_name( $origin $(, $param_name )* ).map(Into::into).map_err(Into::into)
	};

	// no `deposit_event` function wanted
	(@impl_deposit_event
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, I: $instantiable:path)?>;
		$system:ident;
		{ $( $other_where_bounds:tt )* }
	) => {};

	(@impl_deposit_event
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		$system:ident;
		{ $( $other_where_bounds:tt )* }
		$vis:vis fn deposit_event$(<$event_trait_instance:ident $(, $event_instance:ident)?>)?() = default;
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?> $module<$trait_instance $(, $instance)?>
			where $( $other_where_bounds )*
		{
			$vis fn deposit_event(
				event: impl Into<< $trait_instance as $trait_name $(<$instance>)? >::Event>
			) {
				<$system::Module<$trait_instance>>::deposit_event(event.into())
			}
		}
	};

	(@impl_on_initialize
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
		fn on_initialize() -> $return:ty { $( $impl:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OnInitialize<$trait_instance::BlockNumber>
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{
			fn on_initialize(_block_number_not_used: $trait_instance::BlockNumber) -> $return {
				$crate::sp_tracing::enter_span!("on_initialize");
				{ $( $impl )* }
			}
		}
	};

	(@impl_on_initialize
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
		fn on_initialize($param:ident : $param_ty:ty) -> $return:ty { $( $impl:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OnInitialize<$trait_instance::BlockNumber>
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{
			fn on_initialize($param: $param_ty) -> $return {
				$crate::sp_tracing::enter_span!("on_initialize");
				{ $( $impl )* }
			}
		}
	};

	(@impl_on_initialize
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OnInitialize<$trait_instance::BlockNumber>
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{}
	};

	(@impl_on_runtime_upgrade
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
		fn on_runtime_upgrade() -> $return:ty { $( $impl:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OnRuntimeUpgrade
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{
			fn on_runtime_upgrade() -> $return {
				$crate::sp_tracing::enter_span!("on_runtime_upgrade");
				{ $( $impl )* }
			}
		}
	};

	(@impl_on_runtime_upgrade
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OnRuntimeUpgrade
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{}
	};


	(@impl_on_finalize
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
		fn on_finalize() { $( $impl:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OnFinalize<$trait_instance::BlockNumber>
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{
			fn on_finalize(_block_number_not_used: $trait_instance::BlockNumber) {
				$crate::sp_tracing::enter_span!("on_finalize");
				{ $( $impl )* }
			}
		}
	};

	(@impl_on_finalize
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
		fn on_finalize($param:ident : $param_ty:ty) { $( $impl:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OnFinalize<$trait_instance::BlockNumber>
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{
			fn on_finalize($param: $param_ty) {
				$crate::sp_tracing::enter_span!("on_finalize");
				{ $( $impl )* }
			}
		}
	};

	(@impl_on_finalize
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OnFinalize<$trait_instance::BlockNumber>
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{
		}
	};

	(@impl_offchain
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
		fn offchain_worker() { $( $impl:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OffchainWorker<$trait_instance::BlockNumber>
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{
			fn offchain_worker(_block_number_not_used: $trait_instance::BlockNumber) { $( $impl )* }
		}
	};

	(@impl_offchain
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
		fn offchain_worker($param:ident : $param_ty:ty) { $( $impl:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OffchainWorker<$trait_instance::BlockNumber>
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{
			fn offchain_worker($param: $param_ty) { $( $impl )* }
		}
	};

	(@impl_offchain
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		{ $( $other_where_bounds:tt )* }
	) => {
		impl<$trait_instance: $trait_name$(<I>, $instance: $instantiable)?>
			$crate::traits::OffchainWorker<$trait_instance::BlockNumber>
			for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )*
		{}
	};

	// Expansion for _origin_ dispatch functions with no return type.
	(@impl_function
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		$origin_ty:ty;
		$error_type:ty;
		$ignore:ident;
		$(#[doc = $doc_attr:tt])*
		$vis:vis fn $name:ident (
			$origin:ident $(, $param:ident : $param_ty:ty )*
		) { $( $impl:tt )* }
	) => {
		$(#[doc = $doc_attr])*
		#[allow(unreachable_code)]
		$vis fn $name(
			$origin: $origin_ty $(, $param: $param_ty )*
		) -> $crate::dispatch::DispatchResult {
			$crate::sp_tracing::enter_span!(stringify!($name));
			{ $( $impl )* }
			Ok(())
		}
	};

	// Expansion for _origin_ dispatch functions with explicit return type.
	(@impl_function
		$module:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>;
		$origin_ty:ty;
		$error_type:ty;
		$ignore:ident;
		$(#[doc = $doc_attr:tt])*
		$vis:vis fn $name:ident (
			$origin:ident $(, $param:ident : $param_ty:ty )*
		) -> $result:ty { $( $impl:tt )* }
	) => {
		$(#[doc = $doc_attr])*
		$vis fn $name($origin: $origin_ty $(, $param: $param_ty )* ) -> $result {
			$crate::sp_tracing::enter_span!(stringify!($name));
			$( $impl )*
		}
	};

	// Declare a `Call` variant parameter that should be encoded `compact`.
	(@create_call_enum
		$call_type:ident;
		<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?>
		{ $( $other_where_bounds:tt )* }
		{ $( $generated_variants:tt )* }
		{ $( $current_params:tt )* }
		variant $fn_name:ident;
		$( #[doc = $doc_attr:tt] )*
		#[compact]
		$type:ty;
		$( $rest:tt )*
	) => {
		$crate::decl_module! {
			@create_call_enum
			$call_type;
			<$trait_instance: $trait_name $(<I>, $instance: $instantiable $(= $module_default_instance)? )?>
			{ $( $other_where_bounds )* }
			{ $( $generated_variants )* }
			{
				$( $current_params )*
				#[codec(compact)]
				$type,
			}
			variant $fn_name;
			$( #[doc = $doc_attr] )*
			$( $rest )*
		}
	};

	// Declare a `Call` variant parameter.
	(@create_call_enum
		$call_type:ident;
		<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?>
		{ $( $other_where_bounds:tt )* }
		{ $( $generated_variants:tt )* }
		{ $( $current_params:tt )* }
		variant $fn_name:ident;
		$(#[doc = $doc_attr:tt])*
		$type:ty;
		$( $rest:tt )*
	) => {
		$crate::decl_module! {
			@create_call_enum
			$call_type;
			<$trait_instance: $trait_name $(<I>, $instance: $instantiable $(= $module_default_instance)? )?>
			{ $( $other_where_bounds )* }
			{ $( $generated_variants )* }
			{
				$( $current_params )*
				$type,
			}
			variant $fn_name;
			$( #[doc = $doc_attr] )*
			$( $rest )*
		}
	};

	(@create_call_enum
		$call_type:ident;
		<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?>
		{ $( $other_where_bounds:tt )* }
		{ $( $generated_variants:tt )* }
		{ $( $current_params:tt )* }
		variant $fn_name:ident;
		$(#[doc = $doc_attr:tt])*
		$(
			variant $next_fn_name:ident;
			$( $rest:tt )*
		)?
	) => {
		$crate::decl_module! {
			@create_call_enum
			$call_type;
			<$trait_instance: $trait_name $(<I>, $instance: $instantiable $(= $module_default_instance)? )?>
			{ $( $other_where_bounds )* }
			{
				$( $generated_variants )*
				#[allow(non_camel_case_types)]
				$(#[doc = $doc_attr])*
				$fn_name (
					$( $current_params )*
				),
			}
			{}
			$(
				variant $next_fn_name;
				$( $rest )*
			)?
		}
	};

	(@create_call_enum
		$call_type:ident;
		<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?>
		{ $( $other_where_bounds:tt )* }
		{ $( $generated_variants:tt )* }
		{}
	) => {
		/// Dispatchable calls.
		///
		/// Each variant of this enum maps to a dispatchable function from the associated module.
		#[derive($crate::codec::Encode, $crate::codec::Decode)]
		pub enum $call_type<$trait_instance: $trait_name$(<I>, $instance: $instantiable $( = $module_default_instance)?)?>
			where $( $other_where_bounds )*
		{
			#[doc(hidden)]
			#[codec(skip)]
			__PhantomItem($crate::sp_std::marker::PhantomData<($trait_instance, $($instance)?)>, $crate::Never),
			$( $generated_variants )*
		}
	};

	// The main macro expansion that actually renders the module code.

	(@imp
		$(#[$attr:meta])*
		pub struct $mod_type:ident<
			$trait_instance:ident: $trait_name:ident
			$(<I>, $instance:ident: $instantiable:path $(= $module_default_instance:path)?)?
		>
		for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident {
			$(
				$(#[doc = $doc_attr:tt])*
				#[weight = $weight:expr]
				$fn_vis:vis fn $fn_name:ident(
					$from:ident $( , $(#[$codec_attr:ident])* $param_name:ident : $param:ty)*
				) $( -> $result:ty )* { $( $impl:tt )* }
				{ $($fn_instance:ident: $fn_instantiable:path)? }
			)*
		}
		{ $( $other_where_bounds:tt )* }
		{ $( $deposit_event:tt )* }
		{ $( $on_initialize:tt )* }
		{ $( $on_runtime_upgrade:tt )* }
		{ $( $on_finalize:tt )* }
		{ $( $offchain:tt )* }
		{ $( $constants:tt )* }
		{ $error_type:ty }
	) => {
		$crate::__check_reserved_fn_name! { $( $fn_name )* }

		// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
		#[derive(Clone, Copy, PartialEq, Eq, $crate::RuntimeDebug)]
		$( #[$attr] )*
		pub struct $mod_type<
			$trait_instance: $trait_name
			$(<I>, $instance: $instantiable $( = $module_default_instance)?)?
		>($crate::sp_std::marker::PhantomData<($trait_instance, $( $instance)?)>) where
			$( $other_where_bounds )*;

		$crate::decl_module! {
			@impl_on_initialize
			$mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>;
			{ $( $other_where_bounds )* }
			$( $on_initialize )*
		}

		$crate::decl_module! {
			@impl_on_runtime_upgrade
			$mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>;
			{ $( $other_where_bounds )* }
			$( $on_runtime_upgrade )*
		}


		$crate::decl_module! {
			@impl_on_finalize
			$mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>;
			{ $( $other_where_bounds )* }
			$( $on_finalize )*
		}

		$crate::decl_module! {
			@impl_offchain
			$mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>;
			{ $( $other_where_bounds )* }
			$( $offchain )*
		}
		$crate::decl_module! {
			@impl_deposit_event
			$mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>;
			$system;
			{ $( $other_where_bounds )* }
			$( $deposit_event )*
		}

		/// Can also be called using [`Call`].
		///
		/// [`Call`]: enum.Call.html
		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?>
			where $( $other_where_bounds )*
		{
			$(
				$crate::decl_module! {
					@impl_function
					$mod_type<$trait_instance: $trait_name $(<I>, $fn_instance: $fn_instantiable)?>;
					$origin_type;
					$error_type;
					$from;
					$(#[doc = $doc_attr])*
					$fn_vis fn $fn_name (
						$from $(, $param_name : $param )*
					) $( -> $result )* { $( $impl )* }
				}
			)*
		}

		$crate::decl_module! {
			@create_call_enum
			$call_type;
			<$trait_instance: $trait_name $(<I>, $instance: $instantiable $(= $module_default_instance)? )?>
			{ $( $other_where_bounds )* }
			{}
			{}
			$(
				variant $fn_name;
				$(#[doc = $doc_attr])*
				$(
					$(#[$codec_attr])*
					$param;
				)*
			)*
		}

		// Implement weight calculation function for Call
		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::GetDispatchInfo
			for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
		{
			fn get_dispatch_info(&self) -> $crate::dispatch::DispatchInfo {
				match *self {
					$(
						$call_type::$fn_name( $( ref $param_name ),* ) => {
							let base_weight = $weight;
							let weight = <dyn $crate::dispatch::WeighData<( $( & $param, )* )>>::weigh_data(
								&base_weight,
								($( $param_name, )*)
							);
							let class = <dyn $crate::dispatch::ClassifyDispatch<( $( & $param, )* )>>::classify_dispatch(
								&base_weight,
								($( $param_name, )*)
							);
							let pays_fee = <dyn $crate::dispatch::PaysFee<( $( & $param, )* )>>::pays_fee(
								&base_weight,
								($( $param_name, )*)
							);
							$crate::dispatch::DispatchInfo {
								weight,
								class,
								pays_fee,
							}
						},
					)*
					$call_type::__PhantomItem(_, _) => unreachable!("__PhantomItem should never be used."),
				}
			}
		}

		// Implement GetCallName for the Call.
		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::GetCallName
			for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
		{
			fn get_call_name(&self) -> &'static str {
				match *self {
					$(
						$call_type::$fn_name( $( ref $param_name ),* ) => {
							// Don't generate any warnings for unused variables
							let _ = ( $( $param_name ),* );
							stringify!($fn_name)
						},
					)*
					$call_type::__PhantomItem(_, _) => unreachable!("__PhantomItem should never be used."),
				}
			}

			fn get_call_names() -> &'static [&'static str] {
				&[
					$(
						stringify!($fn_name),
					)*
				]
			}
		}

		// manual implementation of clone/eq/partialeq because using derive erroneously requires
		// clone/eq/partialeq from T.
		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::Clone
			for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
		{
			fn clone(&self) -> Self {
				match *self {
					$(
						$call_type::$fn_name( $( ref $param_name ),* ) =>
							$call_type::$fn_name( $( (*$param_name).clone() ),* )
					,)*
					_ => unreachable!(),
				}
			}
		}
		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::PartialEq
			for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
		{
			fn eq(&self, _other: &Self) -> bool {
				match *self {
					$(
						$call_type::$fn_name( $( ref $param_name ),* ) => {
							let self_params = ( $( $param_name, )* );
							if let $call_type::$fn_name( $( ref $param_name ),* ) = *_other {
								self_params == ( $( $param_name, )* )
							} else {
								match *_other {
									$call_type::__PhantomItem(_, _) => unreachable!(),
									_ => false,
								}
							}
						}
					)*
					_ => unreachable!(),
				}
			}
		}
		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::Eq
			for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
		{}

		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::fmt::Debug
			for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
		{
			fn fmt(
				&self,
				_f: &mut $crate::dispatch::fmt::Formatter,
			) -> $crate::dispatch::result::Result<(), $crate::dispatch::fmt::Error> {
				match *self {
					$(
						$call_type::$fn_name( $( ref $param_name ),* ) =>
							write!(_f, "{}{:?}",
								stringify!($fn_name),
								( $( $param_name.clone(), )* )
							)
					,)*
					_ => unreachable!(),
				}
			}
		}

		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::Dispatchable
			for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
		{
			type Trait = $trait_instance;
			type Origin = $origin_type;
			type Info = $crate::weights::DispatchInfo;
			type PostInfo = $crate::weights::PostDispatchInfo;
			fn dispatch(self, _origin: Self::Origin) -> $crate::dispatch::DispatchResultWithPostInfo {
				match self {
					$(
						$call_type::$fn_name( $( $param_name ),* ) => {
							$crate::decl_module!(
								@call
								$from
								$mod_type<$trait_instance $(, $fn_instance)?> $fn_name _origin $system [ $( $param_name ),* ]
							)
						},
					)*
					$call_type::__PhantomItem(_, _) => { unreachable!("__PhantomItem should never be used.") },
				}
			}
		}
		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::Callable<$trait_instance>
			for $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
		{
			type Call = $call_type<$trait_instance $(, $instance)?>;
		}

		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?>
			where $( $other_where_bounds )*
		{
			#[doc(hidden)]
			pub fn dispatch<D: $crate::dispatch::Dispatchable<Trait = $trait_instance, PostInfo = $crate::weights::PostDispatchInfo>>(
				d: D,
				origin: D::Origin
			) -> $crate::dispatch::DispatchResultWithPostInfo {
				d.dispatch(origin)
			}
		}
		$crate::__dispatch_impl_metadata! {
			$mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>
			{ $( $other_where_bounds )* }
			$call_type $origin_type
			{
				$(
					$(#[doc = $doc_attr])*
					fn $fn_name($from $(, $(#[$codec_attr])* $param_name : $param )*);
				)*
			}
		}
		$crate::__impl_module_constants_metadata ! {
			$mod_type<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?>
			{ $( $other_where_bounds )* }
			$( $constants )*
		}

		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::ModuleErrorMetadata
			for $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
		{
			fn metadata() -> &'static [$crate::dispatch::ErrorMetadata] {
				<$error_type as $crate::dispatch::ModuleErrorMetadata>::metadata()
			}
		}
	}
}

pub trait IsSubType<T: Callable<R>, R> {
	fn is_sub_type(&self) -> Option<&CallableCallFor<T, R>>;
}

/// Implement a meta-dispatch module to dispatch to other dispatchers.
#[macro_export]
macro_rules! impl_outer_dispatch {
	(
		$(#[$attr:meta])*
		pub enum $call_type:ident for $runtime:ident where origin: $origin:ty {
			$(
				$module:ident::$camelcase:ident,
			)*
		}
	) => {
		$(#[$attr])*
		#[derive(
			Clone, PartialEq, Eq,
			$crate::codec::Encode,
			$crate::codec::Decode,
			$crate::RuntimeDebug,
		)]
		pub enum $call_type {
			$(
				$camelcase ( $crate::dispatch::CallableCallFor<$camelcase, $runtime> )
			,)*
		}
		impl $crate::dispatch::GetDispatchInfo for $call_type {
			fn get_dispatch_info(&self) -> $crate::dispatch::DispatchInfo {
				match self {
					$( $call_type::$camelcase(call) => call.get_dispatch_info(), )*
				}
			}
		}
		impl $crate::dispatch::GetCallMetadata for $call_type {
			fn get_call_metadata(&self) -> $crate::dispatch::CallMetadata {
				use $crate::dispatch::GetCallName;
				match self {
					$( $call_type::$camelcase(call) => {
						let function_name = call.get_call_name();
						let pallet_name = stringify!($camelcase);
						$crate::dispatch::CallMetadata { function_name, pallet_name }
					}, )*
				}
			}

			fn get_module_names() -> &'static [&'static str] {
				&[$(
					stringify!($camelcase),
				)*]
			}

			fn get_call_names(module: &str) -> &'static [&'static str] {
				use $crate::dispatch::{Callable, GetCallName};
				match module {
					$(
						stringify!($camelcase) =>
							<<$camelcase as Callable<$runtime>>::Call
								as GetCallName>::get_call_names(),
					)*
					_ => unreachable!(),
				}
			}
		}
		impl $crate::dispatch::Dispatchable for $call_type {
			type Origin = $origin;
			type Trait = $call_type;
			type Info = $crate::weights::DispatchInfo;
			type PostInfo = $crate::weights::PostDispatchInfo;
			fn dispatch(
				self,
				origin: $origin,
			) -> $crate::dispatch::DispatchResultWithPostInfo {
				$crate::impl_outer_dispatch! {
					@DISPATCH_MATCH
					self
					$call_type
					origin
					{}
					0;
					$( $camelcase ),*
				}
			}
		}
		$(
			impl $crate::dispatch::IsSubType<$camelcase, $runtime> for $call_type {
				#[allow(unreachable_patterns)]
				fn is_sub_type(&self) -> Option<&$crate::dispatch::CallableCallFor<$camelcase, $runtime>> {
					match *self {
						$call_type::$camelcase(ref r) => Some(r),
						// May be unreachable
						_ => None,
					}
				}
			}

			impl From<$crate::dispatch::CallableCallFor<$camelcase, $runtime>> for $call_type {
				fn from(call: $crate::dispatch::CallableCallFor<$camelcase, $runtime>) -> Self {
					$call_type::$camelcase(call)
				}
			}
		)*
	};
	(@DISPATCH_MATCH
		$self:ident
		$call_type:ident
		$origin:ident
		{ $( $generated:tt )* }
		$index:expr;
		$name:ident
		$( , $rest:ident )*
	) => {
		$crate::impl_outer_dispatch! {
			@DISPATCH_MATCH
			$self
			$call_type
			$origin
			{
				$( $generated )*
				$call_type::$name(call) => call.dispatch($origin),
			}
			$index + 1;
			$( $rest ),*
		}
	};
	(@DISPATCH_MATCH
		$self:ident
		$call_type:ident
		$origin:ident
		{ $( $generated:tt )* }
		$index:expr;
	) => {
		match $self {
			$( $generated )*
		}
	}
}

/// Implement metadata for dispatch.
#[macro_export]
#[doc(hidden)]
macro_rules! __dispatch_impl_metadata {
	(
		$mod_type:ident<$trait_instance:ident: $trait_name:ident$(<I>, $instance:ident: $instantiable:path)?>
		{ $( $other_where_bounds:tt )* }
		$($rest:tt)*
	) => {
		impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?>
			where $( $other_where_bounds )*
		{
			#[doc(hidden)]
			pub fn call_functions() -> &'static [$crate::dispatch::FunctionMetadata] {
				$crate::__call_to_functions!($($rest)*)
			}
		}
	}
}

/// Implement metadata for module constants.
#[macro_export]
#[doc(hidden)]
macro_rules! __impl_module_constants_metadata {
	// Without instance
	(
		$mod_type:ident<$trait_instance:ident: $trait_name:ident>
		{ $( $other_where_bounds:tt )* }
		$(
			$( #[doc = $doc_attr:tt] )*
			$name:ident: $type:ty = $value:expr;
		)*
	) => {
		$crate::paste::item! {
			$crate::__impl_module_constants_metadata! {
				GENERATE_CODE
				$mod_type<$trait_instance: $trait_name>
				{ $( $other_where_bounds )* }
				$(
					$( #[doc = $doc_attr] )*
					[< $name DefaultByteGetter >]
					$name<$trait_instance: $trait_name>: $type = $value;
				)*
			}
		}
	};
	// With instance
	(
		$mod_type:ident<$trait_instance:ident: $trait_name:ident<I>, $instance:ident: $instantiable:path>
		{ $( $other_where_bounds:tt )* }
		$(
			$( #[doc = $doc_attr:tt] )*
			$name:ident: $type:ty = $value:expr;
		)*
	) => {
		$crate::paste::item! {
			$crate::__impl_module_constants_metadata! {
				GENERATE_CODE
				$mod_type<$trait_instance: $trait_name<I>, $instance: $instantiable>
				{ $( $other_where_bounds )* }
				$(
					$( #[doc = $doc_attr] )*
					[< $name DefaultByteGetter >]
					$name<$trait_instance: $trait_name<I>, $instance: $instantiable>: $type = $value;
				)*
			}
		}
	};
	// Do the code generation
	(GENERATE_CODE
		$mod_type:ident<$trait_instance:ident: $trait_name:ident $(<I>, $instance:ident: $instantiable:path)?>
		{ $( $other_where_bounds:tt )* }
		$(
			$( #[doc = $doc_attr:tt] )*
			$default_byte_name:ident
			$name:ident<
				$const_trait_instance:ident: $const_trait_name:ident $(
					<I>, $const_instance:ident: $const_instantiable:path
				)*
			>: $type:ty = $value:expr;
		)*
	) => {
		impl<$trait_instance: 'static + $trait_name $(<I>, $instance: $instantiable)?>
			$mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
		{
			#[doc(hidden)]
			pub fn module_constants_metadata() -> &'static [$crate::dispatch::ModuleConstantMetadata] {
				// Create the `ByteGetter`s
				$(
					#[allow(non_upper_case_types)]
					#[allow(non_camel_case_types)]
					struct $default_byte_name<
						$const_trait_instance: $const_trait_name $(
							<I>, $const_instance: $const_instantiable
						)?
					>($crate::dispatch::marker::PhantomData<
						($const_trait_instance, $( $const_instance)?)
					>);
					impl<$const_trait_instance: 'static + $const_trait_name $(
						<I>, $const_instance: $const_instantiable)?
					> $crate::dispatch::DefaultByte
						for $default_byte_name <$const_trait_instance $(, $const_instance)?>
					{
						fn default_byte(&self) -> $crate::dispatch::Vec<u8> {
							let value: $type = $value;
							$crate::dispatch::Encode::encode(&value)
						}
					}

					unsafe impl<$const_trait_instance: 'static + $const_trait_name $(
						<I>, $const_instance: $const_instantiable)?
					> Send for $default_byte_name <$const_trait_instance $(, $const_instance)?> {}

					unsafe impl<$const_trait_instance: 'static + $const_trait_name $(
						<I>, $const_instance: $const_instantiable)?
					> Sync for $default_byte_name <$const_trait_instance $(, $const_instance)?> {}
				)*
				&[
					$(
						$crate::dispatch::ModuleConstantMetadata {
							name: $crate::dispatch::DecodeDifferent::Encode(stringify!($name)),
							ty: $crate::dispatch::DecodeDifferent::Encode(stringify!($type)),
							value: $crate::dispatch::DecodeDifferent::Encode(
								$crate::dispatch::DefaultByteGetter(
									&$default_byte_name::<
										$const_trait_instance $(, $const_instance)?
									>(
										$crate::dispatch::marker::PhantomData
									)
								)
							),
							documentation: $crate::dispatch::DecodeDifferent::Encode(
								&[ $( $doc_attr ),* ]
							),
						}
					),*
				]
			}
		}
	}
}

/// Convert the list of calls into their JSON representation, joined by ",".
#[macro_export]
#[doc(hidden)]
macro_rules! __call_to_functions {
	(
		$call_type:ident $origin_type:ty
		{
			$(
				$(#[doc = $doc_attr:tt])*
				fn $fn_name:ident($from:ident
					$(
						, $(#[$codec_attr:ident])* $param_name:ident : $param:ty
					)*
				);
			)*
		}
	) => {
		$crate::__functions_to_metadata!(0; $origin_type;; $(
			fn $fn_name( $($(#[$codec_attr])* $param_name: $param ),* );
			$( $doc_attr ),*;
		)*)
	};
}


/// Convert a list of functions into a list of `FunctionMetadata` items.
#[macro_export]
#[doc(hidden)]
macro_rules! __functions_to_metadata{
	(
		$fn_id:expr;
		$origin_type:ty;
		$( $function_metadata:expr ),*;
		fn $fn_name:ident(
			$(
				$(#[$codec_attr:ident])* $param_name:ident : $param:ty
			),*
		);
		$( $fn_doc:expr ),*;
		$( $rest:tt )*
	) => {
		$crate::__functions_to_metadata!(
			$fn_id + 1; $origin_type;
			$( $function_metadata, )* $crate::__function_to_metadata!(
				fn $fn_name($( $(#[$codec_attr])* $param_name : $param ),*); $( $fn_doc ),*; $fn_id;
			);
			$($rest)*
		)
	};
	(
		$fn_id:expr;
		$origin_type:ty;
		$( $function_metadata:expr ),*;
	) => {
		&[ $( $function_metadata ),* ]
	}
}

/// Convert a function into its metadata representation.
#[macro_export]
#[doc(hidden)]
macro_rules! __function_to_metadata {
	(
		fn $fn_name:ident(
			$( $(#[$codec_attr:ident])* $param_name:ident : $param:ty),*
		);
		$( $fn_doc:expr ),*;
		$fn_id:expr;
	) => {
		$crate::dispatch::FunctionMetadata {
			name: $crate::dispatch::DecodeDifferent::Encode(stringify!($fn_name)),
			arguments: $crate::dispatch::DecodeDifferent::Encode(&[
				$(
					$crate::dispatch::FunctionArgumentMetadata {
						name: $crate::dispatch::DecodeDifferent::Encode(stringify!($param_name)),
						ty: $crate::dispatch::DecodeDifferent::Encode(
							$crate::__function_to_metadata!(@stringify_expand_attr
								$(#[$codec_attr])* $param_name: $param
							)
						),
					}
				),*
			]),
			documentation: $crate::dispatch::DecodeDifferent::Encode(&[ $( $fn_doc ),* ]),
		}
	};

	(@stringify_expand_attr #[compact] $param_name:ident : $param:ty) => {
		concat!("Compact<", stringify!($param), ">")
	};

	(@stringify_expand_attr $param_name:ident : $param:ty) => { stringify!($param) };

	(@stringify_expand_attr $(#[codec_attr:ident])* $param_name:ident : $param:ty) => {
		compile_error!(concat!(
			"Invalid attribute for parameter `", stringify!($param_name),
			"`, the following attributes are supported: `#[compact]`"
		));
	}
}

#[macro_export]
#[doc(hidden)]
macro_rules! __check_reserved_fn_name {
	(deposit_event $( $rest:ident )*) => {
		$crate::__check_reserved_fn_name!(@compile_error deposit_event);
	};
	(on_initialize $( $rest:ident )*) => {
		$crate::__check_reserved_fn_name!(@compile_error on_initialize);
	};
	(on_runtime_upgrade $( $rest:ident )*) => {
		$crate::__check_reserved_fn_name!(@compile_error on_runtime_upgrade);
	};
	(on_finalize $( $rest:ident )*) => {
		$crate::__check_reserved_fn_name!(@compile_error on_finalize);
	};
	(offchain_worker $( $rest:ident )*) => {
		$crate::__check_reserved_fn_name!(@compile_error offchain_worker);
	};
	($t:ident $( $rest:ident )*) => {
		$crate::__check_reserved_fn_name!($( $rest )*);
	};
	() => {};
	(@compile_error $ident:ident) => {
		compile_error!(
			concat!(
				"Invalid call fn name: `",
				stringify!($ident),
				"`, name is reserved and doesn't match expected signature, please refer to ",
				"`decl_module!` documentation to see the appropriate usage, or rename it to an ",
				"unreserved keyword."
			),
		);
	};
	(@compile_error_renamed $ident:ident $new_ident:ident) => {
		compile_error!(
			concat!(
				"`",
				stringify!($ident),
				"` was renamed to `",
				stringify!($new_ident),
				"`. Please rename your function accordingly.",
			),
		);
	};
}

#[cfg(test)]
// Do not complain about unused `dispatch` and `dispatch_aux`.
#[allow(dead_code)]
mod tests {
	use super::*;
	use crate::weights::{DispatchInfo, DispatchClass, Pays};
	use crate::traits::{
		CallMetadata, GetCallMetadata, GetCallName, OnInitialize, OnFinalize, OnRuntimeUpgrade
	};

	pub trait Trait: system::Trait + Sized where Self::AccountId: From<u32> {
		type Origin;
		type BlockNumber: Into<u32>;
		type Call: From<Call<Self>>;
	}

	pub mod system {
		use super::*;

		pub trait Trait {
			type AccountId;
		}

		pub fn ensure_root<R>(_: R) -> DispatchResult {
			Ok(())
		}
	}

	decl_module! {
		pub struct Module<T: Trait> for enum Call where origin: T::Origin, T::AccountId: From<u32> {
			/// Hi, this is a comment.
			#[weight = 0]
			fn aux_0(_origin) -> DispatchResult { unreachable!() }

			#[weight = 0]
			fn aux_1(_origin, #[compact] _data: u32,) -> DispatchResult { unreachable!() }

			#[weight = 0]
			fn aux_2(_origin, _data: i32, _data2: String) -> DispatchResult { unreachable!() }

			#[weight = 3]
			fn aux_3(_origin) -> DispatchResult { unreachable!() }

			#[weight = 0]
			fn aux_4(_origin, _data: i32) -> DispatchResult { unreachable!() }

			#[weight = 0]
			fn aux_5(_origin, _data: i32, #[compact] _data2: u32,) -> DispatchResult { unreachable!() }

			#[weight = (5, DispatchClass::Operational)]
			fn operational(_origin) { unreachable!() }

			fn on_initialize(n: T::BlockNumber,) -> Weight { if n.into() == 42 { panic!("on_initialize") } 7 }
			fn on_finalize(n: T::BlockNumber,) { if n.into() == 42 { panic!("on_finalize") } }
			fn on_runtime_upgrade() -> Weight { 10 }
			fn offchain_worker() {}
		}
	}

	const EXPECTED_METADATA: &'static [FunctionMetadata] = &[
				FunctionMetadata {
					name: DecodeDifferent::Encode("aux_0"),
					arguments: DecodeDifferent::Encode(&[]),
					documentation: DecodeDifferent::Encode(&[
						" Hi, this is a comment."
					])
				},
				FunctionMetadata {
					name: DecodeDifferent::Encode("aux_1"),
					arguments: DecodeDifferent::Encode(&[
						FunctionArgumentMetadata {
							name: DecodeDifferent::Encode("_data"),
							ty: DecodeDifferent::Encode("Compact<u32>")
						}
					]),
					documentation: DecodeDifferent::Encode(&[]),
				},
				FunctionMetadata {
					name: DecodeDifferent::Encode("aux_2"),
					arguments: DecodeDifferent::Encode(&[
						FunctionArgumentMetadata {
							name: DecodeDifferent::Encode("_data"),
							ty: DecodeDifferent::Encode("i32"),
						},
						FunctionArgumentMetadata {
							name: DecodeDifferent::Encode("_data2"),
							ty: DecodeDifferent::Encode("String"),
						}
					]),
					documentation: DecodeDifferent::Encode(&[]),
				},
				FunctionMetadata {
					name: DecodeDifferent::Encode("aux_3"),
					arguments: DecodeDifferent::Encode(&[]),
					documentation: DecodeDifferent::Encode(&[]),
				},
				FunctionMetadata {
					name: DecodeDifferent::Encode("aux_4"),
					arguments: DecodeDifferent::Encode(&[
						FunctionArgumentMetadata {
							name: DecodeDifferent::Encode("_data"),
							ty: DecodeDifferent::Encode("i32"),
						}
					]),
					documentation: DecodeDifferent::Encode(&[]),
				},
				FunctionMetadata {
					name: DecodeDifferent::Encode("aux_5"),
					arguments: DecodeDifferent::Encode(&[
						FunctionArgumentMetadata {
							name: DecodeDifferent::Encode("_data"),
							ty: DecodeDifferent::Encode("i32"),
						},
						FunctionArgumentMetadata {
							name: DecodeDifferent::Encode("_data2"),
							ty: DecodeDifferent::Encode("Compact<u32>")
						}
					]),
					documentation: DecodeDifferent::Encode(&[]),
				},
				FunctionMetadata {
					name: DecodeDifferent::Encode("operational"),
					arguments: DecodeDifferent::Encode(&[]),
					documentation: DecodeDifferent::Encode(&[]),
				},
			];

	pub struct TraitImpl {}

	impl Trait for TraitImpl {
		type Origin = u32;
		type BlockNumber = u32;
		type Call = OuterCall;
	}

	type Test = Module<TraitImpl>;

	impl_outer_dispatch! {
		pub enum OuterCall for TraitImpl where origin: u32 {
			self::Test,
		}
	}

	impl system::Trait for TraitImpl {
		type AccountId = u32;
	}

	#[test]
	fn module_json_metadata() {
		let metadata = Module::<TraitImpl>::call_functions();
		assert_eq!(EXPECTED_METADATA, metadata);
	}

	#[test]
	fn compact_attr() {
		let call: Call<TraitImpl> = Call::aux_1(1);
		let encoded = call.encode();
		assert_eq!(2, encoded.len());
		assert_eq!(vec![1, 4], encoded);

		let call: Call<TraitImpl> = Call::aux_5(1, 2);
		let encoded = call.encode();
		assert_eq!(6, encoded.len());
		assert_eq!(vec![5, 1, 0, 0, 0, 8], encoded);
	}

	#[test]
	fn encode_is_correct_and_decode_works() {
		let call: Call<TraitImpl> = Call::aux_0();
		let encoded = call.encode();
		assert_eq!(vec![0], encoded);
		let decoded = Call::<TraitImpl>::decode(&mut &encoded[..]).unwrap();
		assert_eq!(decoded, call);

		let call: Call<TraitImpl> = Call::aux_2(32, "hello".into());
		let encoded = call.encode();
		assert_eq!(vec![2, 32, 0, 0, 0, 20, 104, 101, 108, 108, 111], encoded);
		let decoded = Call::<TraitImpl>::decode(&mut &encoded[..]).unwrap();
		assert_eq!(decoded, call);
	}

	#[test]
	#[should_panic(expected = "on_initialize")]
	fn on_initialize_should_work_1() {
		<Module<TraitImpl> as OnInitialize<u32>>::on_initialize(42);
	}

	#[test]
	fn on_initialize_should_work_2() {
		assert_eq!(<Module<TraitImpl> as OnInitialize<u32>>::on_initialize(10), 7);
	}

	#[test]
	#[should_panic(expected = "on_finalize")]
	fn on_finalize_should_work() {
		<Module<TraitImpl> as OnFinalize<u32>>::on_finalize(42);
	}

	#[test]
	fn on_runtime_upgrade_should_work() {
		assert_eq!(<Module<TraitImpl> as OnRuntimeUpgrade>::on_runtime_upgrade(), 10);
	}

	#[test]
	fn weight_should_attach_to_call_enum() {
		// operational.
		assert_eq!(
			Call::<TraitImpl>::operational().get_dispatch_info(),
			DispatchInfo { weight: 5, class: DispatchClass::Operational, pays_fee: Pays::Yes },
		);
		// custom basic
		assert_eq!(
			Call::<TraitImpl>::aux_3().get_dispatch_info(),
			DispatchInfo { weight: 3, class: DispatchClass::Normal, pays_fee: Pays::Yes },
		);
	}

	#[test]
	fn call_name() {
		let name = Call::<TraitImpl>::aux_3().get_call_name();
		assert_eq!("aux_3", name);
	}

	#[test]
	fn call_metadata() {
		let call = OuterCall::Test(Call::<TraitImpl>::aux_3());
		let metadata = call.get_call_metadata();
		let expected = CallMetadata { function_name: "aux_3".into(), pallet_name: "Test".into() };
		assert_eq!(metadata, expected);
	}

	#[test]
	fn get_call_names() {
		let call_names = Call::<TraitImpl>::get_call_names();
		assert_eq!(["aux_0", "aux_1", "aux_2", "aux_3", "aux_4", "aux_5", "operational"], call_names);
	}

	#[test]
	fn get_module_names() {
		let module_names = OuterCall::get_module_names();
		assert_eq!(["Test"], module_names);
	}
}
